import threading
import socket
import time
from socket import *
import rsa
import hashlib
import logging
import leveldb
import pickle
from mpt_core.mpt import MerklePatriciaTree
from mpt_core.au import presense
from mpt_core.encoding import hexdigest_to_nib
from simchain.simchain.network import Network

HOST = '127.0.0.1'
PORT = 8001
BUFSIZE = 2048
ADDER = (HOST, PORT)
BUFSIZE = 1024
LEVELDBPATH = 'blockchain_db/data'
LATESTBLOCKINFO = [[],]
class ca_server:
    def __init__(self):
        self.ca_server = socket(AF_INET, SOCK_STREAM)
        self.ca_server.bind(ADDER)
        self.ca_server.listen(5)
        self.storage = {}
        self.MPT = MerklePatriciaTree(debug=False,from_scratch=True)
        self.MPT.init_new_root()
        self.pki_chain_net = Network(12)
        self.peer = self.pki_chain_net.peers[0]
        self.wallet = self.peer.wallet
        self.publickey,self.privatekey = rsa.newkeys(512)
        self.client_dict = {}
        self.client_list = []
        self.latest_block_merkle_root = []
        # todo 因为目前CA与区块链系统未分离，所以区块链系统未能广播
        self.latest_mpt_root_hash = []

    def add_in_self_dict(self,data,pubkey):
        self.client_dict[data] = pubkey

    # todo store pubkey in local json
    # def add_pubkeymap_in_json(self,data):
        #with open('./db/key/pubkey.json','w') as f:
            #f
    """
        def generate_keys(self,client):
        print('[*] Generating secret, please hang on.')
        (pubkey, privkey) = rsa.newkeys(512)
        priv_key_file = open('key/send_key/private.pem', 'w')
        priv_key_file.write(privkey.save_pkcs1().decode('utf-8'))
        priv_key_file.close()
        pub_key_file = open('key/send_key/public.pem', 'w')
        pub_key_file.write(pubkey.save_pkcs1().decode('utf-8'))
        pub_key_file.close()
        # return in string
        pubk_str = pubkey.save_pkcs1().decode('utf-8')

        try:
            with open("key/send_key/private.pem", "rb") as f:
                while True:
                    priv_key_data = f.read(BUFSIZE)
                    if priv_key_data:
                        client.send(priv_key_data)
                    else:
                        print("[*] Private Key has transported!")
                        break
            with open("key/send_key/public.pem", "rb") as f:
                while True:
                    public_key_data = f.read(BUFSIZE)
                    if public_key_data:
                        client.send(public_key_data)
                    else:
                        print("[*] Public Key has transported!")
                        break
        except Exception as err:
            print("[*] This is an error", err)

        return pubk_str
    """


    def public_key_in_mpt(self,pubkey_key):

        key_nibble=hexdigest_to_nib(hashlib.sha256(pubkey_key.encode()).hexdigest())
        self.MPT.insert(key_nibble,pubkey_key)
        # Notice: root_hash is a bytes object
        self.latest_mpt_root_hash.append(self.MPT.root_hash)
        return self.MPT.root_hash

    def create_proof_path(self,public_key):
        key_nibble = hexdigest_to_nib(hashlib.sha256(public_key.encode()).hexdigest())
        proof_path = presense(self.MPT.db,key_nibble)
        proof_path = pickle.dumps(proof_path)
        # 返回的是一个对象,将这个对象要返回给A车
        return proof_path

    def remove_authorization(self,name):
        public_key = self.client_dict[name]
        key_nibble = hexdigest_to_nib(hashlib.sha256(public_key.encode()).hexdigest())
        print('[*] delete pubk from mpt_hash')
        self.MPT.remove(key_nibble)
        mpt_root_hash = self.MPT.root_hash
        self.latest_mpt_root_hash.append(self.MPT.root_hash)
        return mpt_root_hash


    # 对发送的消息进行签名
    def signature(self,message):
        print("signature")

    def encrypt_by_public_key(self,message):
        print("signature")

    def decrypt_by_private_key(self,message):
        print("signature")

    def get_public_list(self):
        return self.client_dict


def make_mpthash_in_tx(ca,mpt_root_hash,cnn):

    mpt_tx_id = ca.pki_chain_net.make_random_transactions(k=1,mpt_root_hash=mpt_root_hash)

    msg = "[Message from server->]Your BlockChain Tx ID is:{}！Please Check in BlockChain after 1 min!".format(mpt_tx_id)
    logging.info(msg)
    cnn.send(msg.encode())

    ca.pki_chain_net.make_random_transactions(k=18,mpt_root_hash=None)
    block = ca.pki_chain_net.consensus()
    write_in_db(LEVELDBPATH,block,cnn)
    # todo 因为未能实现CA与区块链系统同时通信，所以此处还需请求CA服务器，后续区块链系统与CA系统分离后实现！
    ca.latest_block_merkle_root.append(block.merkle_root_hash)
    msg = "[Message from server->]New MPT hash value has been recorded in Block whose merkle_root_hash is:{}!You can check the Block to get the New MPT".format(block.merkle_root_hash)
    cnn.send(msg.encode())
    return mpt_tx_id,block.merkle_root_hash

def write_in_db(db_path,block,cnn):
    db = leveldb.LevelDB(db_path)
    try:
        db.Put(block.merkle_root_hash.encode('utf-8'),block.__repr__().encode('utf-8'))
        logging.info("Block has benn recorded in DB successfully!")
    except Exception as err:
        logging.info(err)
        cnn.send(err.encode())

def read_blockchain_db(db_path,key,cnn):
    db = leveldb.LevelDB(db_path)
    try:
        value = db.Get(key)
        print(type(value))
        return value.decode('utf-8')
    except Exception as err:
        logging.info(err)
        cnn.send(str(err).encode())

def return_latest_mpt_hash(cnn,ca_server):
    print(ca_server.latest_mpt_root_hash)
    msg = ca_server.latest_mpt_root_hash[-1]
    cnn.send(msg)

def socket_recv(cnn,ca_server):
    global mpt_root_hash
    while True:
        try:
            request = cnn.recv(1024).decode('utf-8')
            if len(request)==0:
                cnn.close()
                break
            else:
                print('%s接收到来自客户端%s的信息，信息内容为：%s' % (time.strftime("%D %H:%M:%S"),cnn.getpeername(),request))
                if request == 'register':
                    msg = "[Message from server->]Please input your name"
                    cnn.send(msg.encode())
                    name = cnn.recv(BUFSIZE).decode('utf-8')
                    pubkey_value = ca_server.generate_keys(cnn)
                    ca_server.add_in_self_dict(name,pubkey_value)
                    print(ca_server.client_dict)
                    mpt_root_hash = ca_server.public_key_in_mpt(pubkey_value)
                    # 区块链的作用是分布式认证
                    mpt_tx_id,block_merkle_root = make_mpthash_in_tx(ca=ca_server, mpt_root_hash=mpt_root_hash, cnn=cnn)
                    id = (mpt_tx_id,block_merkle_root)
                    encrypt_serialize_id = pickle.dumps(id)
                    cnn.send(encrypt_serialize_id)
                    continue
                elif request == "apply":
                    # todo 公钥加解密
                    msg = "[Message from server->]Please input your name!"
                    cnn.send(msg.encode())
                    name = cnn.recv(BUFSIZE).decode('utf-8')
                    pubkey_value = ca_server.client_dict[name]
                    print(pubkey_value)
                    digital_id = ca_server.create_proof_path(pubkey_value)
                    encrypt_serialize_id = pickle.dumps(digital_id)
                    cnn.send(encrypt_serialize_id)
                elif request == "mpt":
                    msg = "[Message from server->]The latest Block's Merkle Root which include CA TX is:{}".format(ca_server.latest_block_merkle_root[-1])
                    cnn.send(msg.encode('utf-8'))
                    msg = "[Message from server->]You can Query the Block according to the Merkle Root"
                    cnn.send(msg.encode('utf-8'))

                elif request == "remove":
                    msg = "[Message from server->]Please input your name"
                    cnn.send(msg.encode())
                    name = cnn.recv(BUFSIZE).decode('utf-8')
                    print(name)
                    mpt_root_hash = ca.remove_authorization(name = name)

                    # todo! 对应用A的公钥进行签名认证 client
                    make_mpthash_in_tx(ca=ca_server, mpt_root_hash=mpt_root_hash, cnn=cnn)
                    continue

                elif request == "authorization":
                    msg = "[Message from server->]Now MPT hash is:{}!Please check!".format(ca_server.MPT.root_hash().hex())
                    cnn.send(msg.encode())
                    time.sleep(0.5)
                    msg = "[Message from server->]Please enter the mpt_key you have got"
                    cnn.send(msg.encode())
                    mpt_key = cnn.recv(BUFSIZE).decode('utf-8')
                    ca_server.authorization(cnn,mpt_key)
                    continue

                elif request == "query":
                    msg = "[Message from server->]Please input your Block merkle_root_hash!"
                    # 只要是send的都没有encode
                    cnn.send(msg.encode())
                    resp = cnn.recv(BUFSIZE)
                    # todo 后续改为序列化后存入db key:merkle value:block
                    info = read_blockchain_db(LEVELDBPATH,resp,cnn)
                    print(info)
                    msg = "[Message from server->]Latest Block info is:\n" + info
                    cnn.send(msg.encode())
                    continue
                elif request == "latest":
                    return_latest_mpt_hash(cnn,ca)
                elif request == "quit":
                    cnn.close()
                    break
                else:
                    msg = "[Message from server->]Unknown input!Please input again!"
                    cnn.send(msg.encode())
                    continue
        except ConnectionResetError:
            pass

if __name__ == "__main__":
    ca = ca_server()
    while True:
        cnn,addr = ca.ca_server.accept()
        th = threading.Thread(target=socket_recv, args=(cnn,ca),daemon=True)
        th.start()






"""
if __name__ == "__main__":
    ca = ca_server()
    ca.MPT.init_new_root()
    while True:
        cnn, addr = ca.ca_server.accept()
        for i in cnn:
            print(i)
"""




